package seracher;


import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

public class DocSearcher {
    public DocSearcher() {
        //先将索引构造出来
        index.load();
        loadWordStops();
    }
    //要构建
    Index index=new Index();
    //用来存放结果
    //title url desc

    private HashSet<String> stopWords=new HashSet<>();
    //停用词路径
    private final String STOP_WORD_PATH="/install/java_doc_searcher/stop.txt";
    //整体思路就是将输入的内容进行分词，然后在将这些分词，在倒排中拿到倒排拉链
    //将整个倒排拉链，放入到一个数组中
    //然后根据权重
    public List<Result> search(String query) {
        //这里用来存放每一次的结果，不会导致结果进行每次的叠加
        //去掉一些大众词语且没有实际意义的词语，比如空格，换行回车等。
        List<Term> oldTerms = ToAnalysis.parse(query).getTerms();
        List<Term> terms=new ArrayList<>();
        for(Term term:oldTerms) {
            if(stopWords.contains(term.getName())){
                continue;
            }
            terms.add(term);
        }
        List<Term> resultTerms=new ArrayList<>();
        //首先是先进行遍历分词结果，然后不是空的分词添加到一个分词结果中也就是resultTerms中
        for(Term term:terms){
            //这里有问题，
            if(term==null) {
                continue;
            }
            resultTerms.add(term);
        }
        //
        List<List<Weight>> termResult=new ArrayList<>();
        //这里的话去根据一个分词结果去倒排拉链中去查找分词的结果
        //此时遍历的是次为非空的分词集合，resultTerms
        for(Term term:resultTerms){
            String word=term.getName();
            List<Weight> invertedList = index.getInvertedIndex(word);
            //说明这个词在文档中不存在
            if(invertedList==null) {
                continue;
            }
            //将整个集合给添加到termResult中去
            termResult.add(invertedList);
        }
        List<Weight> allTermResult = mergeResult(termResult);
        //按照降序来排序
        allTermResult.sort(new Comparator<Weight>() {
            @Override
            public int compare(Weight o1, Weight o2) {
                return o2.getWeight()-o1.getWeight();
            }
        });

        List<Result> results=new ArrayList<Result>();
        for(Weight weight:allTermResult){
            //进行整理结果
            DocInfo docInfo = index.getDocInfo(weight.getDocId());
            Result result=new Result();
            result.setTitle(docInfo.getTitle());
            result.setUrl(docInfo.getUrl());
            //这里生成一个描述的时候还要使用正则表达式
            result.setDesc(GenDesc(docInfo.getContent(),terms));
            results.add(result);
        }
        return results;
    }

    private String GenDesc(String content, List<Term> terms) {
        //前后分160个字符
        int firstPos=-1;
        for(Term term:terms){
            String word=term.getName();
            //要的就是找到第一个位置
            //将分词的名字进行严格匹配，出现array)结果不出现报红的现象。
            content = content.toLowerCase().replaceAll("\\b"+word+"\\b"," "+word+" ");
            firstPos= content.indexOf(" "+word+" ");
            if(firstPos>=0){
                break;
            }
        }
        if(firstPos==-1){
            //说明没有找到
            if(content.length()>160) {
                return content.substring(0,160)+"...";
            }
            return content+"...";
        }
        String desc="";
        int begin =firstPos<60? 0:firstPos-60;
        if(firstPos+160>content.length()){
            desc=content.substring(begin)+"...";
        } else {
            desc=content.substring(begin,begin+160)+"...";
        }
        for(Term term:terms){
            String word=term.getName();
            //这句话的意思就是忽略大小写，然后进行替换。
            desc = desc.replaceAll("(?i) " + word + " ", "<i> " + word + " </i>");
        }
        return desc;
    }
    public void loadWordStops() {
        try(BufferedReader bufferedReader=new BufferedReader(new FileReader(STOP_WORD_PATH))){
            while(true) {
                String line = bufferedReader.readLine();
                if(line==null) {
                    break;
                }
                stopWords.add(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    static class Pos{
        public int row;
        public int col;

        public Pos(int row, int col) {
            this.row = row;
            this.col = col;
        }
    }
    //这里针对array 和list来进行合并，因为不让同一个文章出现两次
    //首先针对一个每个一行先对docId进行排序
    //然后再将每一行进行添加到最终的结果中，同时让最小的那个小标然后往下面走。
    private List<Weight> mergeResult(List<List<Weight>> source) {
        List<Weight> target=new ArrayList<>();
        //1.每一行按照id进行升序排序
        for(List<Weight> list:source){
            list.sort(new Comparator<Weight>() {
                @Override
                public int compare(Weight o1, Weight o2) {
                  return o1.getDocId()-o2.getDocId();
                };
            });
        }
        //2.然后将每一行的位置加入到队列中去
        PriorityQueue<Pos> queue=new PriorityQueue<>(new Comparator<Pos>() {
            @Override
            public int compare(Pos o1, Pos o2) {
                Weight w1=source.get(o1.row).get(o1.col);
                Weight w2=source.get(o2.row).get(o2.col);
                return w1.getDocId()-w2.getDocId();
            }
        });
        for(int row=0;row<source.size();row++) {
                queue.offer(new Pos(row,0));
        }
        while(!queue.isEmpty()){
            Pos minPos=queue.poll();
            Weight curWeight=source.get(minPos.row).get(minPos.col);
            //之后开始向目标值中添加元素
            if(target.size()>0) {
                //这里直接判断docId是否相等就行了。
                Weight lastWeight=target.get(target.size()-1);
               if(curWeight.getDocId()==lastWeight.getDocId()) {
                   lastWeight.setWeight(lastWeight.getWeight()+curWeight.getWeight());
               } else {
                   target.add(curWeight);
               }
            } else {
                target.add(curWeight);
            }
            //程序走到这里的话进行更新最小值的下标
            Pos newPos=new Pos(minPos.row,minPos.col+1);
            if(newPos.col>=source.get(newPos.row).size()) {
                continue;
            }
            queue.offer(newPos);
        }
        return target;
    }

    public static void main(String[] args) {
        DocSearcher docSearcher = new DocSearcher();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("-> ");
            String query = scanner.next();
            List<Result> results = docSearcher.search(query);
            for (Result result : results) {
                System.out.println("================================");
                System.out.println(result);
            }
        }
    }


}
